using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using ParserGenerator.Extensions;

namespace ParserGenerator.CharacterMapping
{
    [Serializable]
    public sealed class CharMapBuilder
    {
        public CharInterval Universal { get; }

        [NonSerialized]
        private ReadOnlyDictionary<CharClass, CharClass> _classesWrapper;
        private readonly Dictionary<CharClass, CharClass> _classes;
        public ReadOnlyDictionary<CharClass, CharClass> Classes
        {
            get
            {
                if (_classesWrapper == null)
                    _classesWrapper = _classes.AsReadOnly();
                return _classesWrapper;
            }
        }

        public CharMapBuilder()
            : this(CharInterval.Universal)
        {
        }

        public CharMapBuilder(CharInterval universal)
        {
            Universal = universal;
            _classes = new Dictionary<CharClass, CharClass>();
        }

        public CharClass Intern(CharClass value)
        {
            if (value == null)
                throw new ArgumentNullException(nameof(value));
            if (!value.Universal.Equals(Universal))
                throw new ArgumentException(SR.UniversalMismatch, nameof(value));
            CharClass cc;
            if (!_classes.TryGetValue(value, out cc))
            {
                _classes.Add(value, value);
                cc = value;
            }
            return cc;
        }

        public void Clear()
        {
            _classes.Clear();
        }

        public CharMap Build()
        {
            CharMapConstructor constructor = new CharMapConstructor(this);
            CharMap map = constructor.Construct();
            return map;
        }

        public sealed override string ToString()
        {
            int number = 0;
            StringBuilder sb = new StringBuilder();
            foreach (CharClass value in _classes.Values)
            {
                if (number > 0)
                    sb.Append("\n");
                sb.AppendFormat("{0}: {1}", number++, value);
            }
            return sb.ToString();
        }
    }
}
